package grammarLL1;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class Symbol {
	protected final String symbol;
	private static Pattern symbolPattern = Pattern.compile("^[a-zA-Z!$]$", Pattern.DOTALL);
	private static Pattern upperCasePattern = Pattern.compile("^[A-Z]$", Pattern.DOTALL);
	private static Pattern lowerCasePattern = Pattern.compile("^[a-z]$", Pattern.DOTALL);
	public static final String initialSymbol = "S";
	
	public Symbol(String symbol) {
		if (symbol == null || symbol.length() != 1 || !isValid(symbol)) {
			throw new IllegalArgumentException("Must be single character string");
		}
		
		this.symbol = symbol;
	}
		
	public static boolean isNonTerminal(String s) {
		Matcher m;
		
		m = upperCasePattern.matcher(s);
		
		return m.find();
	}
	
	public static boolean isTerminal(String s) {
		Matcher m;
		
		m = lowerCasePattern.matcher(s);
		
		return m.find();
	}
	
	public static boolean isLambda(Symbol s) {
		return "!".equals(s.toString());
	}
	
	private boolean isValid(String symbol) {
		Matcher m;
		
		m = symbolPattern.matcher(symbol);
		
		return m.matches();
	}
	
	public String toString() {
		return symbol;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		
		if ((obj instanceof Symbol) == false) {
			return false;
		}
		
		Symbol other = (Symbol) obj;
		if (symbol == null) {
			if (other.symbol != null)
				return false;
		} else if (!symbol.equals(other.symbol))
			return false;
		return true;
	}
	
	public static Symbol valueOf(String s) {
		if (isNonTerminal(s)) {
			if (Grammar.nonTerminals.containsKey(s)) {
				return Grammar.nonTerminals.get(s);
			} else {
				NonTerminal nt = new NonTerminal(s);
				Grammar.nonTerminals.put(s, nt);
				return nt;
			}
		} else if (isTerminal(s)) {
			if (Grammar.terminals.containsKey(s)) {
				return Grammar.terminals.get(s);
			} else {
				Terminal t = new Terminal(s);
				Grammar.terminals.put(s, t);
				return t;
			}
		} else {
			return Grammar.terminals.get("!");
		}
	} 
}